home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1990-1992 Michael Davidson.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software
- * and its documentation for any purpose and without fee is hereby
- * granted, provided that the above copyright notice appear in all
- * copies and that both that copyright notice and this permission
- * notice appear in supporting documentation.
- *
- * This software is provided "as is" without express or implied warranty.
- */
-
- #include <stdio.h>
- #include <stdarg.h>
- #include <memory.h>
- #include <errno.h>
- #include <unistd.h>
- #include <sys/param.h>
- #include <sys/seg.h>
- #include <sys/types.h>
- #include <sys/immu.h>
- #include <sys/region.h>
- #include <sys/proc.h>
- #include <sys/tss.h>
- #include <sys/v86.h>
- #include <sys/sysi86.h>
- #include <sys/cram.h>
-
- #include "v86mode.h"
- #include "io.h"
-
- #define DEBUG(x)
- #define V86_IO_DEBUG 1
-
- /*
- * V86 mode TSS
- */
- struct tss386 *v86tss;
-
- /*
- * V86 mode extended TSS
- */
- static struct v86xtss *xtss;
-
- /*
- * V86 mode i/o permission bitmap
- *
- * v86init() requires that the xtss structure including
- * the i/o bitmap be contained within a single 4K page.
- * TSS_IO_BITMAP_SIZE is 0x80 which allows i/o addresses
- * up to 0x3ff to be handled directly through the TSS.
- * i/o accesses above 0x3ff always trap into the V86
- * instruction emulation code which uses a full 8K bitmap
- * to keep track of the entire 16 bit i/o space.
- */
- #define MAX_IO_ADDR 0xffff
- #define IO_BITMAP_SIZE 8192
-
- #define TSS_MAX_IO_ADDR 0x3ff
- #define TSS_IO_BITMAP_SIZE ((TSS_MAX_IO_ADDR + 8) / 8)
-
- static unsigned char *io_bitmap;
-
- /*
- * magic location used for cli/sti and pushf/popf emulation
- */
- #define VIRTUAL_FLAGS (*(unsigned int *)0xF4200)
-
- /*
- * V86Init() - initialise the v86 environment
- */
- int
- V86Init()
- {
- struct v86parm v86parms;
- int i;
-
- /*
- * set up parameters for v86init()
- */
- v86parms.magic[0] = V86_MAGIC0;
- v86parms.magic[1] = V86_MAGIC1;
- v86parms.xtssp = 0;
- #if !defined(SVR4)
- v86parms.xtss_off = 0;
- #endif
- v86parms.szxtss = sizeof(struct v86xtss);
- v86parms.szbitmap = TSS_IO_BITMAP_SIZE + 1;
-
- if (sysi86(SI86V86, V86SC_INIT, &v86parms) != 0)
- return -1;
-
- /*
- * v86init() returns a pointer to the xtss structure
- */
- xtss = v86parms.xtssp;
- v86tss = &xtss->xt_tss;
-
- /*
- * set up fields in the xtss that are used by the kernel
- * emulation code
- */
-
- /*
- * this is the location that will be used by the kernel
- * for cli/sti/popf emulation to keep track of the state
- * of the interrupt mask bit in the flags register
- */
- xtss->xt_vflptr = &VIRTUAL_FLAGS;
-
- /*
- * xt_vimask, xt_vitoect and xt_viflag are bitmasks used by the
- * kernel for exception handling as follows
- *
- * xt_vimask is set in user mode and read by the kernel
- * it defines which exceptions will wake up
- * a process sleeping in v86sleep()
- * xt_vitoect is set in user mode and read by the kernel
- * it defines which exceptions should cause a
- * task switch to 386 mode
- * xt_viflag is set by the kernel and cleared in user mode
- * it defines which exceptions are currently pending
- */
-
- /*
- * not using v86sleep() so set vi_mask to V86VI_NONE
- */
- xtss->xt_vimask = V86VI_NONE;
-
- /*
- * switch to 386 mode to handle processor traps in V86 mode and
- * signals that arrive when running in V86 mode
- */
- xtss->xt_vitoect = V86VI_DIV0 /* divide by 0 */
- | V86VI_SGLSTP /* single step */
- | V86VI_BRKPT /* breakpoint */
- | V86VI_OVERFLOW /* overflow */
- | V86VI_BOUND /* bounds trap */
- | V86VI_INVOP /* illegal instruction */
- | V86VI_SIGHDL; /* signal pending */
-
- /*
- * xt_imaskbits is a bitmap of 256 bits that define whether
- * particular 8086 software interrupts should be emulated
- * by the kernel general protection trap handler or should
- * trap to user mode emulation. Bits set to 0 are directly
- * emulated in the kernel, bits set to 1 cause a task switch
- * to 386 mode for emulation
- */
- for (i = 0; i < V86_IMASKBITS; i++)
- #if 0
- xtss->xt_imaskbits[i] = 0;
- #else
- xtss->xt_imaskbits[i] = 0xffffffff;
- #endif
-
- xtss->xt_tss.t_bitmapbase = v86parms.szxtss << 16;
- io_bitmap = (unsigned char *)xtss + v86parms.szxtss;
- memset(io_bitmap, 0xff, IO_BITMAP_SIZE + 1);
-
- return 0;
- }
-
- /*
- * V86MemMap() - map physical addresses into the V86 address space
- */
- V86MemMap(
- paddr_t phys,
- caddr_t virt,
- int len
- )
- {
- struct v86memory v86mem;
-
- v86mem.vmem_cmd = V86MEM_MAP;
- v86mem.vmem_physaddr= phys;
- v86mem.vmem_membase = virt;
- v86mem.vmem_memlen = len;
- return sysi86(SI86V86, V86SC_MEMFUNC, &v86mem);
- }
-
- /*
- * V86MemUnmap() - unmap physical addresses from the V86 address space
- */
- V86MemUnmap(
- paddr_t phys,
- caddr_t virt,
- int len
- )
- {
- struct v86memory v86mem;
-
- v86mem.vmem_cmd = V86MEM_UNMAP;
- v86mem.vmem_physaddr= phys;
- v86mem.vmem_membase = virt;
- v86mem.vmem_memlen = len;
- return sysi86(SI86V86, V86SC_MEMFUNC, &v86mem);
- }
-
- /*
- * V86IOEnable() - enable a range of i/o addresses
- */
- V86IOEnable(
- unsigned ioaddr,
- int n
- )
- {
- if ((ioaddr + n) > MAX_IO_ADDR)
- return -1;
-
- while (--n >= 0)
- {
- int index = ioaddr >> 3;
- int mask = ~(1 << (ioaddr & 7));
-
- io_bitmap[index] &= mask;
- ioaddr++;
- }
-
- return 0;
- }
-
- /*
- * V86IODisable() - disable a range of i/o addresses
- */
- V86IODisable(
- unsigned ioaddr,
- int n
- )
- {
- if ((ioaddr + n) > MAX_IO_ADDR)
- return -1;
-
- while (--n >= 0)
- {
- int index = ioaddr >> 3;
- int mask = 1 << (ioaddr & 7);
-
- io_bitmap[index] |= mask;
- ioaddr++;
- }
-
- return 0;
- }
-
- /*
- * IOEnabled() is a macro which checks the i/o bitmap for permission
- * to make a byte, word or dword access to i/o space.
- * It always fetches an unsigned short from the i/o bitmap (ie 2 bytes)
- * so it may access one byte beyond the end of the real bitmap - this
- * is OK since it is exactly the same mechanism used by the processor
- * itself when checking the i/o bitmap in the TSS. The i/o bitmap
- * is allocated with an extra byte at the end set to 0xff to catch any
- * attempts to access off the end of i/o address space.
- */
-
- #define IOEnabled(addr, mask) ! ( *(unsigned short *)(io_bitmap+(addr>>3)) \
- & (mask << (addr & 7)) )
-
- #define IO_BYTE_MASK 0x01
- #define IO_WORD_MASK 0x03
- #define IO_DWORD_MASK 0x0f
-
- STATIC int V86Exception();
- STATIC int V86Emulate();
- STATIC int V86IOEmulate(byte_t op, word_t io_addr);
- STATIC int V86INTEmulate(unsigned vector);
-
- /*
- * V86Run() - switch to V86 mode
- */
- int
- V86Run()
- {
- register struct v86xtss *x = xtss;
- register struct tss386 *t = v86tss;
- int stat;
- int EnterV86Mode();
-
- x->xt_timer_count = 0;
- x->xt_timer_bound = 1000;
- x->xt_viflag = 0; /* clear any old exception flags */
-
- do
- {
- x->xt_magicstat = XT_MSTAT_PROCESS;
-
- EnterV86Mode();
-
- stat = x->xt_magicstat;
- x->xt_magicstat = XT_MSTAT_NOPROCESS;
-
- if (stat == XT_MSTAT_OPSAVED)
- *(byte_t *)((CS(t) << 4) + IP(t)) = x->xt_magictrap;
-
- /*
- * check if there are any exceptions that we care about
- */
- if (x->xt_viflag & x->xt_vitoect)
- stat = V86Exception();
- else
- stat = V86Emulate();
-
- } while (stat == 0);
-
- return stat;
- }
-
-
- /*
- * V86Exception() - handle V86 mode exceptions
- */
- STATIC int
- V86Exception()
- {
- register struct v86xtss *x = xtss;
- int viflag;
-
- if (x->xt_viflag & V86VI_SIGHDL) /* signal handler */
- {
- x->xt_viflag &= ~V86VI_SIGHDL;
- (*x->xt_hdlr)(x->xt_signo);
- /*
- * set xt_signo to 0 to tell the kernel that there
- * is no longer a pending signal
- */
- x->xt_signo = 0;
- }
-
- if ((viflag = x->xt_viflag & x->xt_vitoect) == 0)
- return 0;
-
- if (viflag & V86VI_DIV0)
- return ERR_V86_DIV0;
-
- if (viflag & V86VI_SGLSTP)
- return ERR_V86_SGLSTP;
-
- if (viflag & V86VI_BRKPT)
- return ERR_V86_BRKPT;
-
- if (viflag & V86VI_OVERFLOW)
- return ERR_V86_OVERFLOW;
-
- if (viflag & V86VI_BOUND)
- return ERR_V86_BOUND;
-
- if (viflag & V86VI_INVOP)
- return ERR_V86_ILLEGAL_OP;
-
- return -1;
- }
-
- /*
- * V86Emulate() - called to handle emulation of instructions
- * that could not be directly executed in V86 mode
- *
- * also handles the emulation of i/o accesses to i/o
- * addresses beyond the limit (0x3ff) of the i/o
- * bitmap in the TSS.
- */
- STATIC int
- V86Emulate()
- {
- register struct tss386 *t = v86tss;
- byte_t *codeseg;
- word_t ip;
- byte_t op;
- word_t io_addr;
- unsigned vector;
- byte_t *sp;
-
- codeseg = (byte_t *)(CS(t) << 4);
- ip = IP(t);
-
- switch (op = codeseg[ip++])
- {
- case 0xe4: /* in al, imm8 */
- case 0xec: /* in al, dx */
- io_addr = (op & 0x08) ? DX(t) : codeseg[ip++];
- DEBUG((V86_IO_DEBUG, "inb(0x%x)\n", io_addr));
- if (IOEnabled(io_addr, IO_BYTE_MASK))
- AL(t) = inb(io_addr);
- else if (! V86IOEmulate(op, io_addr))
- return ERR_V86_ILLEGAL_IO;
- break;
-
- case 0xe5: /* in ax, imm8 */
- case 0xed: /* in ax, dx */
- io_addr = (op & 0x08) ? DX(t) : codeseg[ip++];
- DEBUG((V86_IO_DEBUG, "inw(0x%x)\n", io_addr));
- if (IOEnabled(io_addr, IO_WORD_MASK))
- AX(t) = inw(io_addr);
- else if (! V86IOEmulate(op, io_addr))
- return ERR_V86_ILLEGAL_IO;
- break;
-
- case 0xe6: /* out imm8, al */
- case 0xee: /* out dx, al */
- io_addr = (op & 0x08) ? DX(t) : codeseg[ip++];
- DEBUG((V86_IO_DEBUG, "outb(0x%x, 0x%x)\n", io_addr, AL(t)));
- if (IOEnabled(io_addr, IO_BYTE_MASK))
- outb(io_addr, AL(t));
- else if (! V86IOEmulate(op, io_addr))
- return ERR_V86_ILLEGAL_IO;
- break;
-
- case 0xe7: /* out imm8, [e]ax */
- case 0xef: /* out dx, [e]ax */
- io_addr = (op & 0x08) ? DX(t) : codeseg[ip++];
- DEBUG((V86_IO_DEBUG, "outw(0x%x, 0x%x)\n", io_addr, AX(t)));
- if (IOEnabled(io_addr, IO_WORD_MASK))
- outw(io_addr, AX(t));
- else if (! V86IOEmulate(op, io_addr))
- return ERR_V86_ILLEGAL_IO;
- break;
-
- case 0xcd: /* int N */
- vector = codeseg[ip++];
- SP(t) -= 6;
- sp = (byte_t *) ((SS(t) << 4) + SP(t));
- *(word_t *) (sp + 4) = FL(t);
- *(word_t *) (sp + 2) = CS(t);
- *(word_t *) (sp + 0) = ip;
- vector *= 4;
- ip = *(word_t *)vector;
- CS(t) = *(word_t *)(vector+2);
- break;
-
- case 0xf4: /* hlt */
- return ERR_V86_HALT;
-
- default:
- return ERR_V86_ILLEGAL_OP;
- } /* end opcode switch */
-
- /*
- * since everything was emulated OK update IP in the TSS
- * and return 0 to indicate that it is OK to go back to
- * V86 mode
- */
-
- IP(t) = ip;
- return 0;
- }
-
- /*
- * V86IOEmulate() - i/o emulation
- *
- * this is the common handler for all
- * illegal i/o requests
- *
- * silently ignore attempts to send a non-specific
- * EIO command to the master PIC (ATI VGAWonder
- * BIOS does this ....)
- *
- * fail all other i/o requests
- */
-
- #define IO_WRITE 0x02
- #define IO_WORD 0x01
-
- STATIC int
- V86IOEmulate(
- byte_t op,
- word_t io_addr
- )
- {
- register struct tss386 *t = v86tss;
- static unsigned cmos_addr;
-
- switch (io_addr)
- {
- case 0x20: /* master PIC command port */
- if ((op & ~0x08) == 0xe4 && AL(t) == 0x20)
- return 1;
-
- case 0x70:
- if (op & IO_WRITE)
- {
- cmos_addr = AL(t);
- if (op & IO_WORD)
- cmos_write(cmos_addr, AH(t));
- }
- else
- {
- AL(t) = cmos_addr;
- if (op & IO_WORD)
- AH(t) = cmos_read(cmos_addr);
- }
- return 1;
-
- case 0x71:
- if (op & IO_WRITE)
- cmos_write(cmos_addr, AL(t));
- else
- AL(t) = cmos_read(cmos_addr);
-
- return 1;
-
- default:
- if (op & IO_WRITE)
- fatal(0, "illegal i/o write of %4x to %4x, cs:ip = %4x:%4x\n",
- (op & IO_WORD) ? AX(t) : AL(t), io_addr, CS(t), IP(t));
- else
- fatal(0, "illegal i/o read from %4x, cs:ip = %4x:%4x\n",
- io_addr, CS(t), IP(t));
- }
-
- return 0;
- }
-
- static int cmos_fd = -1;
-
- cmos_write(
- unsigned addr,
- unsigned data
- )
- {
- fatal(0, "illegal write to CMOS, addr = %02x, data = %02x\n",
- addr, data);
- }
-
- cmos_read(
- unsigned addr
- )
- {
- unsigned char data;
-
- if (cmos_fd < 0)
- cmos_fd = open("/dev/cmos", 0);
-
- if (addr >= CMOSSIZE)
- return 0;
-
- if (cmos_fd < 0 || lseek(cmos_fd, (long)addr, 0) != (long) addr
- || read(cmos_fd, &data, 1) != 1)
- fatal(errno, "read from CMOS addr %02x failed\n", addr);
-
- return data;
- }
-
- char *
- V86ErrorMessage(
- int r
- )
- {
- register struct tss386 *t = v86tss;
- static char message[80];
- char *p;
-
- switch (r)
- {
- case ERR_V86_HALT:
- p = "halt instruction";
- break;
- case ERR_V86_DIV0:
- p = "divide by zero trap";
- break;
- case ERR_V86_SGLSTP:
- p = "single step trap";
- break;
- case ERR_V86_BRKPT:
- p = "breakpoint trap";
- break;
- case ERR_V86_OVERFLOW:
- p = "overflow trap";
- break;
- case ERR_V86_BOUND:
- p = "bounds trap";
- break;
- case ERR_V86_ILLEGAL_OP:
- p = "illegal instruction trap";
- break;
- case ERR_V86_ILLEGAL_IO:
- p = "illegal i/o";
- break;
- }
-
- sprintf(message, "%s at %04x:%04x", p, CS(t), IP(t));
-
- return message;
- }
-
- /*ARGSUSED*/
- v86debug(
- int type, /* debug type */
- char *fmt, /* message format string */
- ... /* optional arguments */
- )
- {
- va_list args;
-
- va_start(args, fmt);
- (void) vfprintf(stderr, fmt, args);
- va_end(args);
- }
-